【アップデート】ECSタスク定義を利用したローカル環境でのテスト実行が可能に!
「Docker ComposeとECSタスク定義の2重管理めんどくさい(TдT)」
ECSはフルマネージドのコンテナコントロールプレーンとして、AWS上のコンテナワークロードのデファクトスタンダードと言えます。ただ、EKSとは違ってECSは完全にAWS独自仕様のため、ローカル環境やAWS以外の環境で、その設定情報をそのまま流用できないのが難点でした。
今回、ECS CLIのローカルテスト機能が、思いっきりアップデートされました。
今回のアップデートで、ECSのタスク定義をシームレスにDocker Compose仕様に変換したり、ローカル環境差分を別ファイルで管理することができるようになっているため、ローカル環境でのタスク定義を利用したテストが、非常に簡単になっています。
日常的にECSを使われている方は、是非一度、この機能試していただければと思います。
タスク定義とローカル環境がワッショイワッショイ!!?? ( ゚д゚) ガタッ / ヾ __L| / ̄ ̄ ̄/_ \/ /
ECSのローカルテストの概要
今回、新しくアナウンスされたECSのローカルテスト機能のアップデートですが、正確には、従来あったECS コマンドラインインターフェイスの機能拡張となります。
Amazon ECS コマンドラインインターフェイスの使用 - Amazon Elastic Container Service
Amazon Elastic Container Service (Amazon ECS) コマンドラインインターフェイス (CLI) には、ローカル開発環境からのクラスターおよびタスクの作成、更新、モニタリングを簡素化する高レベルコマンドが用意されています。Amazon ECS CLI では、Docker Compose ファイル (バージョン 1、バージョン 2、バージョン 3) サポートされています。Docker Compose は、マルチコンテナアプリケーションを定義して実行するためによく使用されるオープンソースツールです。
引用:Amazon ECS コマンドラインインターフェイスの使用 - Amazon Elastic Container Service
ECS CLIでは、以前(2019年5月27日)に、ローカルテスト機能の一部(AWS認証情報の確認や、タスクメタデータサービスのエンドポイント利用)が利用可能になっていました。今回のアップデートで、そのローカルテスト機能が大幅に拡張されたことになります。
ECS CLIローカルの関連ドキュメント
ECS CLIのGitHubはこちら。
GitHubのうち、ECSタスクをローカルで動かすREADMEはこちら。
また、AWS側の網羅的なコマンドリファレンスはこちらになります。
AWSの公式ドキュメントも一式更新されているため、下記で記載するコマンドの詳細は、公式ドキュメントを参照ください。
事前準備:ECS CLIのインストール(アップデート)
最初にECS CLIのインストールを行います。ドキュメントは以下に記載があります。macOS、Linux、Windowsに対応しています。ECS CLIを元々使っていた人も、今回のローカルテスト実行には最新版が必要と思われるので、同じく実施しておきましょう。
自分のローカル環境はMacなので、mac用のバイナリをダウンロードし、バイナリフォルダに格納。
$ sudo curl -o /usr/local/bin/ecs-cli https://amazon-ecs-cli.s3.amazonaws.com/ecs-cli-darwin-amd64-latest
バイナリへの実行権限付与。
$ sudo chmod +x /usr/local/bin/ecs-cli
インストールを確認します。
$ ecs-cli --version ecs-cli version 1.15.1 (628b3e6)
基礎編:ECSタスクをローカルで動かしてみる
GitHubのREADMEに基本的な解説があります。
ローカルテスト用のタスク定義(JSON)の用意
最初に、ローカルテストに利用するECSのタスク定義(JSON)を用意します。今回は、WordPressとMySQLをそれぞれコンテナで起動する以下のタスク定義を用意します。
ファイル名wp-mysql.json
で、以下のファイルを作成します。
{ "containerDefinitions": [ { "name": "wordpress", "links": [ "mysql" ], "image": "wordpress", "essential": true, "portMappings": [ { "containerPort": 80, "hostPort": 8080 } ], "memory": 500, "cpu": 10 }, { "environment": [ { "name": "MYSQL_ROOT_PASSWORD", "value": "password" } ], "name": "mysql", "image": "mysql", "cpu": 10, "memory": 500, "essential": true } ], "family": "hello_world" }
タスク定義JSONから、Docker Composeファイルの生成
以下のコマンドで、タスク定義JSONのwp-mysql.json
から、docker composeのYAMLのdocker-compose.wp-mysql.yml
を作成します。
$ ecs-cli local create --task-def-file wp-mysql.json --output docker-compose.wp-mysql.yml INFO[0000] Successfully wrote docker-compose.wp-mysql.yml INFO[0000] Successfully wrote docker-compose.wp-mysql.override.yml
そうすると、以下のdocker composeファイルdocker-compose.wp-mysql.yml
が生成されます!すげぇな!
version: "3.4" services: mysql: environment: AWS_CONTAINER_CREDENTIALS_RELATIVE_URI: /creds ECS_CONTAINER_METADATA_URI: http://169.254.170.2/v3 MYSQL_ROOT_PASSWORD: password image: mysql labels: ecs-local.task-definition-input.type: local ecs-local.task-definition-input.value: /Users/hamada.koji/prj/hamada/ecs-local/wp-mysql.json networks: ecs-local-network: null wordpress: environment: AWS_CONTAINER_CREDENTIALS_RELATIVE_URI: /creds ECS_CONTAINER_METADATA_URI: http://169.254.170.2/v3 image: wordpress labels: ecs-local.task-definition-input.type: local ecs-local.task-definition-input.value: /Users/hamada.koji/prj/hamada/ecs-local/wp-mysql.json links: - mysql networks: ecs-local-network: null ports: - target: 80 published: 8080 networks: ecs-local-network: external: true
ローカル環境での起動
生成されたdocker-compose.wp-mysql.yml
を元に、ローカル環境でコンテナを起動します。初回起動時は、関連イメージのダウンロードも同時に実行されるはずです。
$ ecs-cli local up --task-def-compose docker-compose.wp-mysql.yml INFO[0000] The network ecs-local-network already exists INFO[0000] Created the amazon-ecs-local-container-endpoints container with ID a6ae1a5cfeaa2523137d3ff38a4e25ca732f1f2f2ee267740d5b87188728d209 INFO[0000] Started container with ID a6ae1a5cfeaa2523137d3ff38a4e25ca732f1f2f2ee267740d5b87188728d209 INFO[0000] Using docker-compose.wp-mysql.yml, docker-compose.wp-mysql.override.yml files to start containers Creating ecs-local_mysql_1 ... done Creating ecs-local_wordpress_1 ... done
起動確認は、ecs-cli local ps
コマンドで実施します。このとき、元ネタとなっているwp-mysql.json
を指定しないと、プロセスが見えないので注意が必要です。
wordpressコンテナは、ローカルホストの8080を受けて要ることなどもわかります。
$ ecs-cli local ps -f wp-mysql.json CONTAINER ID IMAGE STATUS PORTS NAMES TASKDEFINITION a98576f9210a wordpress Up 4 minutes 0.0.0.0:8080->80/tcp /ecs-local_wordpress_1 /Users/hamada.koji/prj/hamada/ecs-local/wp-mysql.json c7509cda504c mysql Up 4 minutes :0->3306/tcp, :0->33060/tcp /ecs-local_mysql_1 /Users/hamada.koji/prj/hamada/ecs-local/wp-mysql.json
この時、docker ps
コマンドを実行すると、WordpressとMySQLコンテナ以外に、ecs localのエンドポイントを提供するコンテナが起動しているのが特徴です。
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a98576f9210a wordpress "docker-entrypoint.s…" 7 minutes ago Up 7 minutes 0.0.0.0:8080->80/tcp ecs-local_wordpress_1 c7509cda504c mysql "docker-entrypoint.s…" 7 minutes ago Up 7 minutes 3306/tcp, 33060/tcp ecs-local_mysql_1 a6ae1a5cfeaa amazon/amazon-ecs-local-container-endpoints "/local-container-en…" 7 minutes ago Up 7 minutes 80/tcp amazon-ecs-local-container-endpoints
動作確認
ブラウザでlocalhost:8080
にアクセスすると、無事、WordPressの初期設定画面が起動します。よござんす!!
ここまで、元ネタはECSのタスク定義のJSONだけだったのですが、それだけを元にして、ローカルでの起動が確認できました。
実践編:超絶便利!!AWS上ECSタスク定義からの直接ローカル実行
以上、タスク定義JSONから、ECSローカルによる起動を確認してみました。ただ、既存のECS環境から、直接ECSローカルを起動してみたという人も多いと思います。特に、WebコンソールやCloudFormationでタスク定義を登録している場合、別途タスク定義のJSONを用意するのは、存外めんどくさいものです。
実は、上述したecs-cli local up
コマンドには、ECSタスク定義のARNを直接利用して、ローカルでタスク(コンテナ)を起動するオプションがあります。これがもう、超絶便利。
事前に、ECSタスク定義をWebコンソールなどで登録しておきます。
以下のコマンドでARNを取得します。事前にAWS CLIと認証情報が設定されていることが条件となります。以下のコマンド例では、タスク名がfargate-php-task
の、最新リビジョンのARNを取得しています。
$ aws ecs describe-task-definition --task-definition fargate-php-task --query "taskDefinition.taskDefinitionArn" --output text arn:aws:ecs:ap-northeast-1:123456789012:task-definition/fargate-php-task:2
ARNを取得できたら、以下のコマンドでECSローカルを起動します。オプションの--task-def-remote
でARNを指定します。
$ ecs-cli local up --task-def-remote arn:aws:ecs:ap-northeast-1:123456789012:task-definition/fargate-php-task:2 INFO[0000] Reading task definition from fargate-php-task:2 INFO[0000] Task Definition network mode is ignored when running containers locally. Tasks will be run in the ecs-local-network. networkMode=awsvpc WARN[0000] awslogs log driver is ignored when running locally. Tasks will default to json-file instead. This can be changed in your compose override file. INFO[0000] Successfully wrote docker-compose.ecs-local.yml INFO[0000] Successfully wrote docker-compose.ecs-local.override.yml INFO[0000] The network ecs-local-network already exists INFO[0000] The amazon-ecs-local-container-endpoints container already exists with ID a6ae1a5cfeaa2523137d3ff38a4e25ca732f1f2f2ee267740d5b87188728d209 INFO[0000] Started container with ID a6ae1a5cfeaa2523137d3ff38a4e25ca732f1f2f2ee267740d5b87188728d209 INFO[0000] Using docker-compose.ecs-local.yml, docker-compose.ecs-local.override.yml files to start containers Creating ecs-local_php-container_1 ... done
おっしゃ、これで動作確認するぞ!と思ったんですが、うまく動きません。もともとこのタスク定義はFargate用のタスク定義のため、ホストインスタンス側のポート設定がされていないものでした。
docker ps
で確認すると、ホストインスタンスとのポートマッピングが設定されていないことがわかります。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 47e53adde965 hamako9999/list-environments-php-container:latest "docker-php-entrypoi…" About a minute ago Up About a minute 80/tcp ecs-local_php-container_1
こういった場合の回避策として、Docker Composeではローカル環境用の設定をオーバーライド(上書き)するための機能が存在します。
上記コマンド(ecs local up)を実行したとき、サーバーのARNからECSローカルが直接起動しているわけではなく、ローカル側で、Docker Compose用のファイルが2つ自動的に生成されています(特にオプションでファイル名を指定しない場合)。
- docker-compose.ecs-local.yml
version: "3.4" services: php-container: environment: AWS_CONTAINER_CREDENTIALS_RELATIVE_URI: /creds ECS_CONTAINER_METADATA_URI: http://169.254.170.2/v3 image: hamako9999/list-environments-php-container:latest labels: ecs-local.task-definition-input.type: remote ecs-local.task-definition-input.value: arn:aws:ecs:ap-northeast-1:123456789012:task-definition/fargate-php-task:2 logging: driver: awslogs options: awslogs-group: /ecs/fargate-php-task awslogs-region: ap-northeast-1 awslogs-stream-prefix: ecs networks: ecs-local-network: null networks: ecs-local-network: external: true
- docker-compose.ecs-local.override.yml
version: "3.4" services: php-container: environment: AWS_CONTAINER_CREDENTIALS_RELATIVE_URI: /creds logging: driver: json-file
今回は、docker composeファイルにポート指定がないため、ローカルホストとのポートマッピングをdocker-compose.ecs-local.override.yml
に、以下のように追記します。
version: "3.4" services: php-container: environment: AWS_CONTAINER_CREDENTIALS_RELATIVE_URI: /creds logging: driver: json-file ports: - target: 80 published: 9000
オーバーライドファイルを作成したら、先程のコマンドに--override
オプションで、作成したオーバーライドファイルを指定します。
$ ecs-cli local up --task-def-remote arn:aws:ecs:ap-northeast-1:123456789012:task-definition/fargate-php-task:2 --override docker-compose.ecs-local.override.yml
docker ps
コマンドで、ホストインスタンス側のポート9000がコンテナにマッピングされていることを確認します。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 16e12bcfbd9d hamako9999/list-environments-php-container:latest "docker-php-entrypoi…" 7 seconds ago Up 6 seconds 0.0.0.0:9000->80/tcp ecs-local_php-container_1
このコンテナは、localhost/phpinfo.php
で環境変数の一覧を返します。今回はポート9000をマッピングしているため、localhost:9000/phpinfo.php
にアクセスし、無事コンテナにアクセスできればOK!!
「ECSタスク定義のみによるコンテナ環境管理が可能に!!」
当たり前の話ですが、ECSのタスク定義はAWS上で動作することを前提として設定します。JSONファイルも独自仕様です。その定義ファイルをそのままローカル環境で使うことはできないため、ローカル環境独自のDocker Composeファイルを用意し運用している現場も多いと思います。特に、複数コンテナが1タスクに入る場合(非常におおくある)場合は、Docker Composeファイルの別管理はほぼ必須でした。
今回のECS CLIのローカル機能拡張により、ECSのタスク定義から自動でDocker Composeファイルを生成し、さらにローカル環境との設定差分を管理する機能が組み込まれました。これによりAWS上のECSタスク定義をシームレスにローカル環境で使うことができます。
だいたい、ここらへんのシェルを設定ファイルを用意しておけば大丈夫そうです。
- タスク定義の最新バージョンのARNを自動的に取得
- 取得したARNからECSローカルでコンテナ起動
- クラウド側とローカル側の差分設定のみをオーバーライドファイルとして用意
- DB接続先などを各ローカル環境設定ファイルとして用意
フルマネージドのECSは設定ファイルも完全独自仕様だったので、こんな形でローカル環境での流用が可能になるとは思いませんでした。開発環境のコンテナ設定管理を非常に簡略化できる可能性があると思うので、一度ECS CLIローカル、試してみていただければと思います。
それでは、今日はこのへんで。濱田(@hamako9999)でした。